package tester;

import java.io.*;

/**
 * Test a single case.
 * The tested program should use standard input and output.
 * Make sure programFile, inputFile, outputFile, workDir DO exist, otherwise
 * an internal exception will be thrown.
 */

public class SingleCaseTester {
  private final Person person;
  private final Problem problem;
  // the following names should be absolute filenames.
  private String programName;
  // in milliseconds
  private int timeout;
  private Comparator comparator;
  // Files
  private File programFile;
  private File inputFile;
  private File outputFile;
  private File workDir;

  private String randomTempFileName() {
    return "$temp$" + (int)(Math.random() * 9000 + 999);
  }

  public TestCaseResult runTest()
      throws InternalErrorException {
    // check if it's OK to run
    if (!checkRunningCondition())
      throw new InternalErrorException("File Not Ready");

    ExeRunner.ExecuteResult er;
    try {
      InputStream inputStream = new FileInputStream(inputFile);
      er = ExeRunner.execute(workDir, timeout, inputStream, programName);
      inputStream.close();

      TestCaseResult result;
      if (er.getTimeUsed() > timeout) {
        result = new TestCaseResult(TestCaseResult.ExitCode.TIME_OUT, timeout, 0);
      } else {
        // if not timed out
        FileInputStream stdOutput = new FileInputStream(outputFile);
        InputStream processInputStream = er.getInputStream();
        if (processInputStream == null) {
          // if program terminate abnormally.
          result = new TestCaseResult(TestCaseResult.ExitCode.ERROR, er.getTimeUsed(), 0);
        }
        else {
          // if program terminate normally, check the result.
          result = new TestCaseResult(TestCaseResult.ExitCode.NORMAL, er.getTimeUsed(),
              comparator.compare(inputFile,
                  processInputStream, stdOutput, person, problem));
          processInputStream.close();
        }
        stdOutput.close();
      }
      er.getInputStream().close();
      return result;

    } catch (InternalErrorException e) {
      throw e;
    } catch (IOException e) {
      throw new InternalErrorException("IOException");
    } catch (Exception e) {
      throw new InternalErrorException("Unknown Error: " + e.getMessage());
    }
  }

  // check if everything is ready for running
  private boolean checkRunningCondition() {
    // create working dir when needed.
    if (!workDir.exists())
      workDir.mkdirs();
    return (programFile.exists() && inputFile.exists() &&
        outputFile.exists() && workDir.exists());
  }

  public SingleCaseTester(Person person, Problem problem,
                          String programName, String workDirName,
                          String inputFileName, String outputFileName,
                          int waitTime, Comparator comparator) {
    this.problem = problem;
    this.person = person;
    this.programName = programName;
    this.timeout = waitTime;
    this.comparator = comparator;
    workDir = new File(workDirName);
    programFile = new File(programName);
    inputFile = new File(inputFileName);
    outputFile = new File(outputFileName);
  }

}
